#include "CUitl.h"

#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <algorithm>
#include <chrono>
#include <mutex>
#include <thread>

namespace CUitl {

char *pbase64 = NULL;
std::mutex cuitlmutex_;
bool str2json(std::string str, Json::Value &v) {
  Json::Reader reader;
  Json::Value temp;
  if (!reader.parse(str, temp)) return false;
  v = temp;
  return true;
}
std::string json2str(Json::Value &v, bool needFormat) {
  std::lock_guard<std::mutex> lk(cuitlmutex_);
  if (needFormat) {
    return v.toStyledString();
  }
  Json::FastWriter FastWriter;
  return FastWriter.write(v);
}

bool file2json(std::string path, Json::Value &v) {
  std::lock_guard<std::mutex> lk(cuitlmutex_);
  v.clear();
  struct stat buffer;
  int ret = stat(path.c_str(), &buffer);
  if (ret != 0) {
    return false;
  }
  Json::Reader r;
  std::ifstream ifs(path.c_str());
  if (!ifs.is_open()) {
    return false;
  }
  if (!r.parse(ifs, v)) {
    ifs.close();
    return false;
  }
  ifs.close();
  return true;
}

bool str2file(std::string path, const std::string data) {
  std::lock_guard<std::mutex> lk(cuitlmutex_);
  FILE *fp = fopen(path.c_str(), "w");
  if (fp) {
    fwrite(data.data(), data.size(), 1, fp);

    fclose(fp);
    sync();
    return true;
  }
  return false;
}

void StringReplace(std::string &strBase, std::string strSrc,
                   std::string strDes) {
  std::string::size_type pos = 0;
  std::string::size_type srcLen = strSrc.size();
  std::string::size_type desLen = strDes.size();
  pos = strBase.find(strSrc, pos);
  while ((pos != std::string::npos)) {
    strBase.replace(pos, srcLen, strDes);
    pos = strBase.find(strSrc, (pos + desLen));
  }
}

uint64_t gettimestatmp() {
  return std::chrono::duration_cast<std::chrono::seconds>(
             std::chrono::system_clock::now().time_since_epoch())
      .count();
}

uint64_t gettimestatmpms() {
  return std::chrono::duration_cast<std::chrono::milliseconds>(
             std::chrono::system_clock::now().time_since_epoch())
      .count();
}

std::string timestamp2str(time_t t) {
  struct tm *local_time;
  local_time = localtime(&t);
  char buf[80];
  std::strftime(buf, 80, "%Y-%m-%d %H:%M:%S", local_time);
  return std::string(buf);
}

time_t str2timestamp(std::string time) {
  struct tm stm;
  strptime(time.c_str(), "%Y-%m-%d %H:%M:%S", &stm);
  time_t t = mktime(&stm);
  return t;
}

std::string GetSystemUuid() {
  std::lock_guard<std::mutex> lk(cuitlmutex_);
  char uuid[39] = {0};
  int fd = open("/proc/sys/kernel/random/uuid", O_RDONLY);
  if (fd >= 0) {
    int ret = read(fd, uuid, 36);
    close(fd);
  }
  return std::string(uuid);
}

size_t getfilesize(const std::string &file_name) {
  std::ifstream in(file_name.c_str());
  in.seekg(0, std::ios::end);
  size_t size = in.tellg();
  in.close();
  return size;  //单位是：byte
}

bool getfileexist(const std::string &file_name) {
  std::ifstream f(file_name.c_str());
  return f.good();
}

int GetRandomNum(int min, int max) {
  srand(static_cast<unsigned int>(time(0)));
  return ((random() % (max - min)) + min);
}

std::string data2base64(const char *data, int size) {
  // char *pbase64 = (char *)calloc(size / 3 * 4 + 8, sizeof(char));
  if (pbase64 == NULL) {
    pbase64 = (char *)calloc(512 * 1024, sizeof(char));
  }
  mg_base64_encode((const unsigned char *)data, size, pbase64);
  int len = 0;
  if (size % 3 == 0) {
    len = size / 3 * 4;
  } else {
    len = (size / 3 + 1) * 4;
  }
  std::string res(pbase64, len);
  // free(pbase64);
  return res;
}
int base642data(std::string &str, char *data) {
  int ret = 0;
  int length = str.size();

  ret = mg_base64_decode((const unsigned char *)str.c_str(), str.size(), data);
  if (ret == str.size()) {
    if (std::string::npos != str.find("==", length - 4)) {
      length = length / 4 * 3 - 2;
    } else if (std::string::npos != str.find("=", length - 4))
      length = length / 4 * 3 - 1;
    else
      length = length / 4 * 3;
  } else {
    return 0;
  }
  return length;
}

std::string url_encode(const char *data, int size) {
  mg_str mgstr_in, mgstr_out;
  mgstr_in.p = data;
  mgstr_in.len = size;
  mgstr_out = mg_url_encode(mgstr_in);
  return std::string(mgstr_out.p, mgstr_out.len);
}

int SaveFile(const char *buff, int len, const char *filename) {
  FILE *stream;
  if ((stream = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "Cannot open output file.\n");
    return 1;
  }
  fwrite(buff, len, 1, stream);
  fclose(stream);
  return 0;
}

bool AddDir(std::string dir) {
  std::string cmd = std::string("mkdir -p ");
  cmd += dir;
  system(cmd.c_str());
  return true;
}

std::string GetMd5(const char *data, int len) {
  cs_md5_ctx md5_ctx;
  cs_md5_init(&md5_ctx);
  cs_md5_update(&md5_ctx, (const unsigned char *)data, len);
  uint8_t digest[16];
  cs_md5_final(digest, &md5_ctx);

  char md5[32];
  cs_to_hex(md5, (const unsigned char *)digest, 16);
  return std::string(md5, 32);
}

int fm_systembyvf(const char *cmdstring) {
  pid_t pid;
  int status;

  if (cmdstring == NULL) {
    return (1);  //如果cmdstring为空，返回非零值，一般为1
  }

  if ((pid = vfork()) < 0) {
    status = -1;  // fork失败，返回-1
  } else if (pid == 0) {
    execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
    // exec执行失败返回127，注意exec只在失败时才返回现在的进程，成功的话现在的进程就不存在啦~~
    _exit(127);
  } else {
    // note: 父进程
    while (waitpid(pid, &status, 0) < 0) {
      if (errno != EINTR) {
        status = -1;  //如果waitpid被信号中断，则返回-1
        break;
      }
    }
  }
  return status;  //如果waitpid成功，则返回子进程的返回状态
}

int fm_systembyio(const char *cmdstring, std::string &result) {
  FILE *fp;
  int res;
  char buf[1024] = {0};
  if (cmdstring == NULL) {
    printf("system cmd is NULL!\n");
    return -1;
  }
  if ((fp = popen(cmdstring, "r")) == NULL) {
    perror("popen");
    printf("popen error: %s/n", strerror(errno));
    return -1;
  } else {
    while (fgets(buf, sizeof(buf), fp)) {
      printf("%s", buf);
      result.append(buf);
    }
    if ((res = pclose(fp)) == -1) {
      printf("close popen file pointer fp error!\n");
      return res;
    } else if (res == 0) {
      return res;
    } else {
      printf("popen res is :%d\n", res);
      return res;
    }
  }
}

}  // namespace CUitl